package com.netnovelstore.controller;

import cn.hutool.json.JSONObject;
import com.alipay.api.AlipayApiException;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.netnovelstore.annotation.NotNullAndBlank;
import com.netnovelstore.annotation.ResultInterceptor;
import com.netnovelstore.config.AlipayConfig;
import com.netnovelstore.config.JwtUtil;
import com.netnovelstore.dao.OrdersMapper;
import com.netnovelstore.dao.UserMapper;
import com.netnovelstore.pojo.AliPay;
import com.netnovelstore.pojo.Exception.FailureBehaviorException;
import com.netnovelstore.pojo.Exception.NotDataException;
import com.netnovelstore.pojo.Order;
import com.netnovelstore.pojo.Result;
import com.netnovelstore.pojo.User;
import com.netnovelstore.util.RedisUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@ResultInterceptor
@RestController
@RequestMapping("/pay")
@Api(tags = "支付宝接口")
@Slf4j
@Validated
public class AliPayController {

    private static final String GATEWAY_URL = "https://openapi.alipaydev.com/gateway.do";
    private static final String FORMAT = "JSON";
    private static final String CHARSET = "UTF-8";
    //签名方式
    private static final String SIGN_TYPE = "RSA2";

    @Resource
    private AlipayConfig aliPayConfig;

    @Resource
    private OrdersMapper ordersMapper;

    @Resource
    private UserMapper userMapper;

    @Resource
    private RedisUtil redisUtil;

    private boolean tokenHandle(String token) throws Exception {
        if (token == null || token.equals("")) {
            throw new NotDataException("UserLoginInterceptor class token is not exist");
        }
        //管理员用
        if (token.equals(JwtUtil.secret))
            return true;
        // 验证 token
        String sub = JwtUtil.validateToken(token);
        if (sub == null || sub.equals("")) {
            throw new NotDataException("token exist, but not same");
        }
        return true;
    }

    @ApiOperation("支付地址")
    @GetMapping("/{trace_no}/{total_amount}/{subject}/{token}")
    // http://localhost:8080/pay/trace_no/total_amount/subject
    @ApiImplicitParams({
            @ApiImplicitParam(value = "订单号(唯一)", name = "trace_no", dataType = "String", paramType = "query"),
            @ApiImplicitParam(value = "金额", name = "total_amount", dataType = "Integer", paramType = "query"),
            @ApiImplicitParam(value = "物品名", name = "subject", dataType = "String", paramType = "query"),
            @ApiImplicitParam(value = "token", name = "token", dataType = "String", paramType = "query"),
    })
    public Result<String> pay(HttpServletResponse httpResponse, @PathVariable(name = "trace_no") String traceNo, @PathVariable(name = "total_amount") Integer totalAmount, @PathVariable(name = "subject") String subject, @PathVariable(name = "token") String token) throws Exception {
        tokenHandle(token);
        User user = (User) redisUtil.get(token);
        if (user == null) {
            log.warn("[AdminInterceptor] has not this user");
            httpResponse.sendError(403, "[AdminInterceptor] has not this user");
        }
        if (user.getUserNumber() == 0) {
            throw new NotDataException("user number error");
        }
        // 1. 创建Client，通用SDK提供的Client，负责调用支付宝的API
        AlipayClient alipayClient = new DefaultAlipayClient(GATEWAY_URL, aliPayConfig.getAppId(),
                aliPayConfig.getAppPrivateKey(), FORMAT, CHARSET, aliPayConfig.getAlipayPublicKey(), SIGN_TYPE);

        // 2. 创建 Request并设置Request参数
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();  // 发送请求的 Request类
        request.setNotifyUrl(aliPayConfig.getNotifyUrl());
        request.setReturnUrl(aliPayConfig.getReturnUrl());
        JSONObject bizContent = new JSONObject();
        bizContent.set("out_trade_no", traceNo);  // 我们自己生成的订单编号
        bizContent.set("total_amount", totalAmount); // 订单的总金额
        bizContent.set("subject", subject);   // 支付的名称
        bizContent.set("user_number", user.getUserNumber());   // 支付的名称
        bizContent.set("product_code", "FAST_INSTANT_TRADE_PAY");  // 固定配置

        AliPay aliPay = new AliPay();
        aliPay.setTraceNo(traceNo);
        aliPay.setSubject(subject);
        aliPay.setTotalAmount(totalAmount);
        aliPay.setUserNumber(user.getUserNumber());

        if (!ordersMapper.insertOrder(aliPay)) {
            throw new FailureBehaviorException("may be trace_no is repeated");
        }

        request.setBizContent(bizContent.toString());

        // 执行请求，拿到响应的结果，返回给浏览器
        String form = "";
        try {
            form = alipayClient.pageExecute(request).getBody(); // 调用SDK生成表单
        } catch (AlipayApiException e) {
            e.printStackTrace();
        }
        httpResponse.setContentType("text/html;charset=" + CHARSET);
        httpResponse.getWriter().write(form);// 直接将完整的表单html输出到页面
        httpResponse.getWriter().flush();
        httpResponse.getWriter().close();
        return Result.ok();
    }

    @NotNullAndBlank
    @Transactional(rollbackFor = Exception.class)
    @ApiOperation("支付回调")
    @PostMapping("/notify")  // 注意这里必须是POST接口
    public String payNotify(HttpServletRequest request) throws Exception {
        if ("TRADE_SUCCESS".equals(request.getParameter("trade_status"))) {
            log.info("=========支付宝异步回调========");
            Map<String, String> params = new HashMap<>();
            Map<String, String[]> requestParams = request.getParameterMap();
            for (String name : requestParams.keySet()) {
                params.put(name, request.getParameter(name));
                // log.info(name + " = " + request.getParameter(name));
            }

            String outTradeNo = params.get("out_trade_no");
            String gmtPayment = params.get("gmt_payment");
            String alipayTradeNo = params.get("trade_no");
            log.info("outTradeNo " + outTradeNo);
            log.info("gmtPayment " + gmtPayment);
            log.info("alipayTradeNo " + alipayTradeNo);

            String sign = params.get("sign");
            String content = AlipaySignature.getSignCheckContentV1(params);
            boolean checkSignature = AlipaySignature.rsa256CheckContent(content, sign, aliPayConfig.getAlipayPublicKey(), "UTF-8"); // 验证签名
            log.info("===========================[ payNotify ]====================================");
            // 支付宝验签
            if (checkSignature) {
                // 验签通过
                log.info("交易名称: " + params.get("subject"));
                log.info("交易状态: " + params.get("trade_status"));
                log.info("支付宝交易凭证号: " + params.get("trade_no"));
                log.info("商户订单号: " + params.get("out_trade_no"));
                log.info("交易金额: " + params.get("total_amount"));
                log.info("买家在支付宝唯一id: " + params.get("buyer_id"));
                log.info("买家付款时间: " + params.get("gmt_payment"));
                log.info("买家付款金额: " + params.get("buyer_pay_amount"));

                Integer totalAmount = Integer.valueOf(60);
                Order order = ordersMapper.selectOrderByTraceNo(params.get("out_trade_no"));
                log.info(order.toString());
                if (order != null) {
                    order.setBuyerId("支付宝id")
                            .setBuyerPayAmount(Double.valueOf(60))
                            .setTradeStatus("已支付");
                    log.info(order.toString());
                    if (!ordersMapper.updateOrdersByTraceNo(order)) {
                        throw new FailureBehaviorException("update order defeat");
                    }
                    User user = userMapper.getUserByNumber(order.getUserNumber());
                    if (user == null) {
                        throw new NotDataException("user is null");
                    }
                    user.setBalance(user.getBalance() + totalAmount);
                    if (!userMapper.updateUserByNumber(user)) {
                        throw new FailureBehaviorException("update defeat");
                    }

                    log.info("===========================[ success ]====================================");
                }

            }
        }
        return "success";
    }
}